package interfaces

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"logs"
	"net/http"
	"os"
	"path/filepath"
	"time"

	"encoding/json"

	"strconv"

	"io/ioutil"

	"mime/multipart"

	"errors"

	"github.com/gorilla/websocket"
)

type ServerResponse struct {
	Method  string      `json:"Method"`
	Code    int         `json:"Code"`
	Message string      `json:"Message"`
	Result  interface{} `json:"Result"`
}
type ClientRequest struct {
	Method string      `json:"Method"`
	Args   interface{} `json:"Args"`
}

// 在线测试：http://www.websocket.org/echo.html
// ws://emb.mobi/wsLogin?account=10001&password=123456

const (
	//----------------- 普通接口

	// account = "13580452503" //13580452503 注意，帐号不是userID！
	account  = "15521133823" //15521133823
	password = "123456"
	userID   = "100000002"
	// talkerID     = "100000002" //100000005 100000006是群
	// talkerIsUser = true
	talkerID     = "100000005" //100000005 100000006是群
	talkerIsUser = false
	pageSize     = "20"

	//----------------- websocket  Websocket协议不是HTTP协议！
	wsIP = "127.0.0.1:8001"

	// wsIP = "emb.mobi"
	// wsIP = "172.18.0.9:8001"
	// wsIP = "ficow.cn:8001"
	// wsIP       = "ficow.cn:8023"
	secure = ""
)

func WsLogin() {

	url := fmt.Sprintf("ws"+secure+"://%s/wsLogin?account=%s&password=%s", wsIP, account, password)
	ws, _, err := websocket.DefaultDialer.Dial(url, nil)

	if err != nil {
		println("dial err:", err.Error())
		return
	}
	go processMessages(ws)

	reader := bufio.NewReader(os.Stdin)
	for isOnline {
		showChoices()
		data, _, _ := reader.ReadLine()
		choice, err := strconv.Atoi(string(data))
		if err != nil {
			println("输入有误！")
			continue
		}
		req := makeRequest(choice)
		println("---------------- 请求参数 --------------------")
		JSON, _ := json.MarshalIndent(req, "", " ")
		logs.Print(string(JSON))
		println("--------------------------------------------")
		err = ws.WriteJSON(req)
		if err != nil {
			return
		}
		println("\nChose:\n", choice, "\n")
	}
}
func showChoices() {
	println(`
----------  INPUT API CHOICE  ----------
	1./sendTextMessage
	2./chatMessageList
	3./sessionList
	4./createGroup
	5./addGroupMemberList
	6./addGroupMember
	7./deleteGroupMember
	8./alterPwd
	9./alterIcon
	10./getIcon
	11./getFile
	------------------------------------------`)
}
func makeRequest(choice int) (req ClientRequest) {

	defer func() {
		data, _ := json.Marshal(req)
		fmt.Println(string(data))
	}()
	switch choice {
	case 1:
		{
			req := map[string]interface{}{
				"talkerID":     talkerID,
				"talkerIsUser": talkerIsUser,
				"msgContent":   "测试文本消息" + time.Now().String(),
			}
			return ClientRequest{Method: "/sendTextMessage", Args: req}
		}

	case 2:
		{
			req := map[string]interface{}{
				"talkerID":     talkerID,
				"talkerIsUser": talkerIsUser,
				"pageIndex":    0,
				"pageSize":     pageSize,
			}
			return ClientRequest{Method: "/chatMessageList", Args: req}
		}
	case 3:
		{
			req := map[string]interface{}{
				"userID": userID,
			}
			return ClientRequest{Method: "/sessionList", Args: req}
		}
	case 4:
		{
			req := map[string]interface{}{
				"nickname": "一个没用的群",
				"公告":       "新进群的改备注",
			}
			return ClientRequest{Method: "/createGroup", Args: req}
		}
	case 5:
		{
			req := map[string]interface{}{
				"groupID":   "100000006",
				"pageIndex": 0,
				"pageSize":  pageSize,
			}
			return ClientRequest{Method: "/addGroupMemberList", Args: req}
		}
	case 6:
		{
			req := map[string]interface{}{
				"groupID":   "100000006",
				"memberIDs": []string{"100000004", "100000007", "100000008"},
			}
			return ClientRequest{Method: "/addGroupMember", Args: req}
		}
	case 7:
		{
			req := map[string]interface{}{
				"groupID":   "100000006",
				"memberIDs": []string{"100000004", "100000007", "100000008"},
			}
			return ClientRequest{Method: "/deleteGroupMember", Args: req}
		}
	case 8:
		{
			req := map[string]interface{}{
				"oldPwd": "123456",
				"newPwd": "ficowshen",
			}
			return ClientRequest{Method: "/alterPwd", Args: req}
		}
	case 9:
		{
			err := newfileUploadRequest("http"+secure+"://"+wsIP+"/alterIcon", map[string]string{"token": token}, "icon", "../../src/public/favicon.ico")
			if err != nil {
				logs.Print(err.Error())
			}
		}
	case 10:
		{
			err := getFile("http"+secure+"://"+wsIP+"/icon/default.ico", "default.ico")
			if err != nil {
				logs.Print(err.Error())
			}
		}
	case 11:
		{
			err := getFile("http"+secure+"://"+wsIP+"/"+base64FileName+"?token="+token, "")
			if err != nil {
				logs.Print(err.Error())
			}
		}
	}
	return ClientRequest{Method: "", Args: nil}
}

var token string
var base64FileName string

func getToken(res *ServerResponse) {
	if res.Method != "/token" {
		return
	}
	result, ok := res.Result.(map[string]interface{})
	if !ok {
		println("req.Args to map failed")
		return
	}
	Token, ok := result["Token"].(string)
	if !ok {
		println("interface{} to string failed")
		return
	}
	token = Token
}

func getBase64FileName(res *ServerResponse) {
	if res.Method != "/receiveMessage" {
		return
	}
	result, ok := res.Result.(map[string]interface{})
	if !ok {
		println("req.Args to map failed")
		return
	}
	Type, ok := result["ContentType"].(string)
	if !ok {
		println("ContentType interface{} to string failed")
		return
	}
	if Type == "text" {
		return
	}
	content, ok := result["Content"].(map[string]interface{})
	if !ok {
		println("Content interface{} to map failed")
		return
	}
	msg, ok := content["Msg"].(string)
	if !ok {
		println("Msg interface{} to string failed")
		return
	}
	base64FileName = msg
}

var isOnline = true

func processMessages(ws *websocket.Conn) {
	for {
		var res ServerResponse
		err := ws.ReadJSON(&res)
		JSON, _ := json.MarshalIndent(res, "", " ")
		getToken(&res)
		getBase64FileName(&res)

		if err != nil {
			println("!!! connection closed.\n!!! read error:\n!!!", err.Error())
			isOnline = false
			ws.Close()
			return
		}
		println("---------------- ServerResponse --------------------")
		println(string(JSON))
		println("----------------------------------------------------")
		showChoices()
	}
}

// Creates a new file upload http request with optional extra params
func newfileUploadRequest(uri string, form map[string]string, formFileName, path string) error {
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile(formFileName, filepath.Base(path))
	if err != nil {
		return err
	}
	_, err = io.Copy(part, file)
	if err != nil {
		return err
	}

	for key, val := range form {
		err = writer.WriteField(key, val)
		if err != nil {
			return err
		}
	}
	err = writer.Close()
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", uri, body)
	req.Header.Add("Content-Type", writer.FormDataContentType())
	if err != nil {
		return err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	Body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	fmt.Println(string(Body))
	return nil
}

func getFile(uri string, fileName string) error {
	resp, err := http.Get(uri)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return err
		}
		return errors.New("statusCode:" + fmt.Sprint(resp.StatusCode) + "\n" + string(body))
	}
	if fileName == "" {
		if resp.Header["Filename"] != nil {
			fileName = resp.Header["Filename"][0]
		} else {
			fileName = "notFound"
		}
	}
	icon, err := os.Create(fileName)
	if err != nil {
		return err
	}
	_, err = io.Copy(icon, resp.Body)
	if err != nil {
		return err
	}
	logs.Print(resp.Header)
	return nil
}
